mori
A library for using ClojureScript's persistent data structures and supporting API from the comfort of vanilla JavaScript.
Rationale
JavaScript is a powerful and flexible dynamic programming language with a beautiful simple associative model at its core. However this design comes at the cost of ubiquitous mutability. Mori embraces the simple associative model but leaves mutability behind. Mori delivers the following benefits to JavaScript:
- Efficient immutable data structures - no cloning required
- Uniform iteration for all types
- Value based equality
Modern JavaScript engines like V8, JavaScriptCore, and SpiderMonkey deliver the performance needed to implement persistent data structures well.
Immutability
Mori delivers highly tuned persistent data structures based on the ones provided in Clojure. When using Mori data structures and operations you do not need to defensively clone as you often do in JavaScript. By providing immutable data structures, Mori encourages value oriented programming.
Mori is not an island
Beyond the the core philosophy Mori makes no other assumptions about how you might use it. In fact, Mori has changed some aspects of the ClojureScript API in order to better accomodate usage from JavaScript and languages that target JavaScript like CoffeeScript. For example, where it makes sense, Mori returns regular JavaScript arrays so that you can use destructuring syntax where available. The following example is in CoffeeScript:
m = mori; [a, b] = m.juxt(m.inc, m.dec)(1); // a = 2, b = 0
Using Mori
Mori can be used in precisely the same way that
libraries like Underscore.js are used. Mori of course
provides not only the same functionality but it
delivers a rich set of data structures.
Mori's operators are more general than the ones
provided by Underscore.js - they work on nearly all
the native JavaScript types. In those cases where they
do not work you usually just need to wrap the type
in mori.primSeq(...)
.
Notation
Each entry is annotated with the signature with an
accompanying description and some relevant
examples. Optional parameters appear
as [option]
in the signature. Sometimes
the signature or description will mention coll
for collection. In general collections can be
converted in to sequences for manipulation via the
various sequence operations. This include all Mori
collections as well as JavaScript Arrays and Strings.
All the examples are in JavaScript or CoffeeScript. However, for simplicity's sake we show the results of operations in Clojure literal syntax notation.
(1 2 3 4) ;; list [1 2 3 4] ;; vector {"foo" 1, "bar" 2} ;; hash map / sorted-map #{"cat" "bird" "dog"} ;; set / sorted-set
Fundamentals
equals mori.equals(x, y)
Test whether two values are equal. Works on all Mori collections. Note that two seqable values will be tested on deep equality of their contents.
var l0 = mori.list(1,2,3); var l1 = mori.list(1,2,3); mori.equals(l0, l1); // => true var v = mori.vector(1,2,3); mori.equals(v, l0); // => true var m0 = mori.toClj({foo: 1}); var m1 = mori.toClj({foo: 1}); mori.equals(m0, m1); // => true
hash mori.hash(x)
Returns the hash code for a value. Values for
which mori.equals
returns true have identical
hash codes.
var l = mori.list(1, 2, 3); var v = mori.vector(1, 2, 3); mori.hash(l) == mori.hash(v); // => true
Type Predicates
isList mori.isList(coll)
Test if something is a list-like collection. Lists support efficient adding to the head.
isSeq mori.isSeq(coll)
Test if something is a sequence (i.e. iterable)
isVector mori.isVector(coll)
Test if something is a vector-like collection. Vectors support random access. It is efficient to add to the end of a vector.
isMap mori.isMap(coll)
Test if something is a map-like collection. Maps support random access and arbitrary keys.
isSet mori.isSet(coll)
Test if something is a hash set.
isCollection mori.isCollection(coll)
Test if something is a collection - lists, maps, sets, vectors are all collections.
isSequential mori.isSequential(coll)
Test if something is sequential. For example vectors are sequential but are not sequences. They can however be converted into something iterable by calling seq on them.
isAssociative mori.isAssociative(coll)
Test if something is associative - i.e. vectors and maps.
isCounted mori.isCounted(coll)
Test if something can give its count in O(1) time.
isIndexed mori.isIndexed(coll)
Test if something is indexed - i.e. vectors.
isReduceable mori.isReduceable(coll)
Test if something is reduceable.
isSeqable mori.isSeqable(coll)
Test if something can be coerced into something iterable.
isReversible mori.isReversible(coll)
Test if something can be reversed in O(1) time.
Collections
list mori.list(arg0, arg1, ...)
Constructs an immutable list. Lists support efficient
addition at the head of the list. It's important to
remember that the cost of operations like
mori.nth
will be linear in the size of
the list.
var l = mori.list(2,3); mori.cons(1, l); // => (1 2 3)
vector mori.vector(arg0, arg1, ...)
Constructs an immutable vector. Vectors support efficient
addition at the end. They also support efficient random
access. You will probably use mori.vector
much
more often than mori.list
var v = mori.vector(1,2,3,4); mori.nth(v, 0); // => 1
hashMap mori.hashMap(key0, val0, key1, val1, ...)
Constructs an immutable hash map. Unlike JavaScript objects Mori PersistentHashMap support complex keys. It's recommended that you only use immutable values for your keys - numbers, strings or a Mori collection.
var m0 = mori.hashMap("foo", 1, "bar", 2); // m0 = {"foo" 1, "bar" 2} mori.get(m0, "bar"); // => 2 var m1 = mori.assoc(m0, m.vector(1,2), 3); // m1 = {"foo" 1, "bar" 2, [1 2] 3} mori.get(m1, m.vector(1,2)); // => 3
sorted_map mori.sorted_map(key0, val0, key1, val1, ...)
Like a hash map but keeps its keys ordered.
mori.sorted_map(2, "two", 3, "three", 1, "one"); // {1 "one", 2 "two", 3 "three"}
set mori.set(seqable)
Constructs a collection of unique items. You may pass in any seqable type - this includes JavaScript arrays and strings. There are several operations unique to sets which do not apply to the other collections.
var s = mori.set(["bird", "cat", "dog"]); mori.conj(s, "dog"); // => #{"bird" "cat" "dog"} mori.conj(s, "zebra"); // => #{"bird" "cat" "dog" "zebra"}
sortedSet mori.sortedSet(arg0, arg1, ...)
Like set but keeps its elements ordered.
mori.sortedSet(3,2,1,3); // #{1 2 3}
range mori.range([start], [end], [step])
Construct a potentially infinite lazy range of values.
With no parameters, a infinite lazy sequence starting at 0 will be returned.
var r = mori.range(); // => (0 1 2 3 …)
A single parameter serves as the end
(exclusive) argument.
var r = mori.range(10); // => (0 1 2 3 4 5 6 7 8 9)
Two parameters serve as start
and
end
.
var r = mori.range(1,5); // => (1 2 3 4)
If three parameters are specified, the third serves
as a step
argument.
var r = mori.range(1,9,2); // => (1 3 5 7)
queue mori.queue(arg0, arg1, ...)
Constructs a persistent queue. Queues support efficient addition at the end and removal from the front.
var q0 = mori.queue(1, 2); // => #queue [1 2] var q1 = mori.conj(q0, 3); // => #queue [1 2 3] mori.peek(q1); // => 1 mori.pop(q1); // => #queue [2 3] mori.rest(q1); // => (2 3)
Collection Operations
conj mori.conj(coll, arg0, arg1, ...)
Add something to a collection. The behavior depends on the type of the collection.
var l = mori.list(2,3); mori.conj(l, 1); // => (1 2 3) var v = mori.vector(1,2); mori.conj(v, 3); // => [1 2 3] var m = mori.hashMap("foo", 1); mori.conj(m, mori.vector("bar", 2));; // => {"foo" 1 "bar" 2} var s = mori.set(["cat", "bird", "dog"]); mori.conj(s, "zebra"); // => #{"cat" "bird" "dog" "zebra"}
into mori.into(coll, from)
Add all the items in the second collection to the first one as
if calling mori.conj
repeatedly.
var l = mori.list(2,3); var v = mori.vector(1,2); mori.into(l, v); // => (2 1 2 3) mori.into(l, l); // => (3 2 2 3) mori.into(v, l); // => [1 2 2 3] mori.into(v, v); // => [1 2 1 2]
assoc mori.assoc(coll, key0, val0, key1, val1, ...)
Associate a new key-value pair in an associative collection. Works on vectors and maps.
var v = mori.vector("foo", "bar", "baz"); mori.assoc(v, 1, "quux"); // => ["foo" "quux" "baz"] var m = mori.hashMap("foo", 1); mori.assoc(m, "bar", 2); // => {"foo" 1 "bar" 2} mori.assoc(m, "foo", 6); // => {"foo" 6}
dissoc mori.dissoc(coll, key0, key1, ...)
Removes keys from an associative collection. Works on maps.
var m = mori.hashMap("foo", 1, "bar", 2, "baz", 3); mori.dissoc(m, "bar", "baz"); // => {"foo" 1}
distinct mori.distinct(coll)
Returns a sequence of the elements of coll with duplicates removed.
var v = mori.vector(1, 1, 2, 3, 3, 4, 5); mori.distinct(v); // => (1 2 3 4 5)
empty mori.empty(coll)
Remove everything from a collection.
var m = mori.hashMap("foo", 1, "bar", 2, "baz", 3); mori.empty(m); // => {} var v = mori.vector("foo", "bar", "baz"); mori.empty(v); // => []
get mori.get(coll, key, [not-found])
Retrieve a value from a collection.
var v = mori.vector("foo", "bar", "baz"); mori.get(v, 1); // "bar" var m = mori.hashMap("foo", 1, "bar", 2); mori.get(m, "foo"); // => 1 mori.get(m, "baz", "nope"); // => "nope"
getIn mori.getIn(coll, keys, [not-found])
Retrieve a value from a nested collection. keys
may be any seqable object.
var v = mori.vector("foo", "bar", "baz"); var v2 = mori.vector("quux", v); mori.getIn(v2, [1, 2]); // => "baz" var m = mori.hashMap("foo", 1, "bar", 2); var m2 = mori.hashMap("baz", 3, "quux", m); mori.getIn(m2, ["quux", "bar"]); // => 2
hasKey mori.hasKey(coll, key)
Returns true if the collection has the given key/index. Otherwise, returns false.
var v = mori.vector("foo", "bar", "baz"); mori.hasKey(v, 1); // => true mori.hasKey(v, 9); // => false var m = mori.hashMap("foo", 1, "bar", 2); mori.hasKey(m, "foo"); // => true mori.hasKey(m, "quux"); // => false var s = mori.set(["foo", "bar", "baz"]); mori.hasKey(s, "foo"); // => true mori.hasKey(s, "quux"); // => false
find mori.find(coll, key)
Returns the key value pair as an array for a given key. Returns null if that key isn't present.
var v = mori.vector("foo", "bar", "baz") mori.find(v, 2) // => [2, "baz"] mori.find(v, 9) // null var m = mori.hashMap("foo", 1, "bar", 2) mori.find(m, "foo") // => ["foo", 1] mori.find(m, "quux") // null
nth mori.nth(coll, index)
Get the value at the specified index. Complexity
depends on the collection. nth
is essentially
constant on vector, but linear on lists. For collections
which are not sequential like sets and hash-map, the
collection will be coerced into a sequence first.
var v = mori.vector("foo", "bar", "baz"); mori.nth(v, 1); // => "bar"
last mori.last(coll)
Get the last value in a collection, in linear time.
var v = mori.vector("foo", "bar", "baz"); mori.last(v); // => "baz"
assocIn mori.assocIn(coll, keys, val)
Convenience function for assoc'ing nested associative
data structures. keys
may be any seqable.
var h = mori.hashMap("foo", mori.hashMap("bar", 1)); mori.assocIn(h, ["foo", "baz"], 2); // => {"foo" {"bar" 1, "baz" 2}}
updateIn mori.updateIn(coll, keys, function)
var h = mori.hashMap("foo", mori.vector(1, 2, 3)); mori.updateIn(h, ["foo", 1], mori.inc); // => {"foo" [1 3 3]}
count mori.count(coll)
Returns the length of the collection.
var l = mori.list("foo", "bar", "baz"); mori.count(l) // 3 var v = mori.vector("foo", "bar", "baz"); mori.count(v) // 3 var s = mori.set(["foo", "bar", "baz"]); mori.count(s) // 3 var m = mori.hashMap("foo", 1, "bar", 2); mori.count(m) // 2
isEmpty mori.isEmpty(coll)
Returns true if the collection is empty.
var l = mori.list("foo", "bar", "baz"); mori.isEmpty(l); // => false var v = mori.vector(); mori.isEmpty(v); // => true
peek mori.peek(coll)
Returns either the first item of a list or the last item of a vector.
var l = mori.list("foo", "bar", "baz"); mori.peek(l) // "foo" var v = mori.vector("foo", "bar", "baz"); mori.peek(v) // "baz"
pop mori.pop(coll)
Returns either a list with the first item removed or a vector with the last item removed.
var l = mori.list("foo", "bar", "baz"); mori.pop(l) // ("bar" "baz") var v = mori.vector("foo", "bar", "baz"); mori.pop(v) // ["foo" "bar"]
zipmap mori.zipmap(seqable0, seqable1)
Takes two seqable objects and constructs a hash map. The first seqable provides the keys, the second seqable the values.
var keys = ["foo", "bar", "baz"]; var vals = [1, 2, 3]; var h = mori.zipmap(keys, vals); // => {"foo" 1, "bar" 2, "baz" 3}
reverse mori.reverse(coll)
Returns a reversed sequence of a collection.
var v = mori.vector("foo", "bar", "baz"); mori.reverse(v); // => ["baz", "bar", "foo"]
Vector Operations
subvec mori.subvec(vector, start, [end])
Returns a subsection of a vector in constant time.
var v = mori.vector("cat", "dog", "bird", "zebra"); mori.subvec(v,1,2); // => ["dog"]
Hash Map Operations
keys mori.keys(map)
Returns the keys of a hash map as a sequence.
var m = mori.hashMap("foo", 1, "bar", 2); mori.intoArray(mori.keys(m)); // => [ "foo", "bar" ]
vals mori.values(map)
Returns the values of a hash map as a sequence.
var m = mori.hashMap("foo", 1, "bar", 2); mori.intoArray(mori.vals(m)); // => [ 1, 2 ]
merge mori.merge(map, m0, m1, ...)
Returns the result of conj-ing the rest of the maps into the first map. If any of the keys exist in the previous map, they will be overridden.
var m = mori.hashMap("foo", 1, "bar", 2); mori.merge(m, mori.hashMap("bar", 3, "baz", 4)); // => {foo 1, bar 3, baz 4}
Set Operations
disj mori.disj(set)
Removes an element from a set.
var s = mori.set(["cat", "dog", "bird"]); // => #{"cat" "bird" "dog"} mori.disj(s,"bird"); // => #{"dog" "cat"}
union mori.union(set0, set1, ...)
Returns the union of two sets.
var s0 = mori.set(["cat", "dog"]); var s1 = mori.set(["zebra", "lion"]); mori.union(s0, s1); // => #{"lion" "cat" "dog" "zebra"}
intersection mori.intersection(set0, set1, ...)
Returns the intersection of two sets.
var s0 = mori.set(["cat", "dog", "mouse"]); var s1 = mori.set(["dog", "cat", "bird"]); mori.intersection(s0, s1); // => #{"cat" "dog"}
difference mori.difference(set0, set1, ...)
Returns the difference between two sets.
var s0 = mori.set(["cat", "dog", "mouse"]); var s1 = mori.set(["dog", "cat", "bird"]); mori.difference(s0, s1); // => #{"mouse" "bird"}
isSubset mori.isSubset(seta, setb)
Returns true if seta
is a subset of setb
.
var s0 = mori.set(["dog", "cat"]); var s1 = mori.set(["cat", "dog", "bird"]); mori.isSubset(s0, s1); // => true
isSuperset mori.isSuperset(seta, setb)
Returns true if seta
is a superset of setb
.
var s0 = mori.set(["cat", "dog", "bird"]); var s1 = mori.set(["dog", "cat"]); mori.isSuperset(s0, s1); // => true
Sequences
first mori.first(coll)
Returns the first element in a collection.
mori.first("foobar"); // => "f" mori.first([1,2,3]); // => 1 var l = mori.list(1,2,3); mori.first(l); // => 1 var m = mori.hashMap("foo", 1, "bar", 2); mori.first(m); // some key-value pair as an array
rest mori.rest(coll)
Returns the remaining elements in a collection.
mori.rest("foobar"); // => ("o" "o" "b" "a" "r") mori.rest([1,2,3]); // => (2 3) var l = mori.list(1,2,3); mori.rest(l); // => (2 3) var m = mori.hashMap("foo", 1, "bar", 2); mori.rest(m); // remaining key-value pairs
seq mori.seq(coll)
Converts a collection whether Mori or JavaScript primitive into a sequence.
mori.seq("foo"); // => ("f" "o" "o") mori.seq(mori.list()); // => null
cons mori.cons(val, coll)
Converts a collection into a sequence and adds a value to the front.
var v = mori.vector(2, 3); mori.cons(1, v); // => (1 2 3)
concat mori.concat(coll0, coll1, ...)
Converts its arguments into sequences and concatenates them.
var r = mori.range(3); var a = [3, 4, 5]; var l = mori.list(6, 7); var v = mori.vector(8, 9); mori.concat(r, a, l, v); // => (0 1 2 3 4 5 6 7 8 9)
flatten mori.flatten(coll)
Converts an arbitrarily nested collection into a flat sequence.
var v = mori.toClj([[1, 2], 3, [4], [[5, 6], 7]]); mori.flatten(v); // => (1 2 3 4 5 6 7)
intoArray mori.intoArray(seq)
Converts a seqable collection, including Mori seqs back into a JavaScript array. Non-lazy.
var lazy = mori.map(mori.inc, [1,2,3]); mori.intoArray(lazy); // => [2,3,4]
each mori.each(coll, f)
Iterate over a collection. For side effects.
var xs = mori.map(mori.inc, [1,2,3]); mori.each(xs, function(n) { console.log(n); }); // will print 2 then 3 then 4 at your JS console
map mori.map(f, coll0, coll1, ...)
Return a lazy sequence that represents
the original collection with f
applied to
each element. Note that map can take multiple collections
This obviates the need for Underscore.js's zip.
var a0 = [1,2,3]; mori.map(mori.inc, a0); // => (2 3 4) var a1 = [4,5,6]; var a2 = [7,8,9]; mori.map(mori.vector, a0, a1, a2); // => ([1 4 7] [2 5 8] [3 6 9])
mapcat mori.mapcat(f, coll0, coll1, ...)
Applies f
, which must return a collection, to
each element of the original collection(s) and concatenates
the results into a single sequence.
var a = mori.seq("abc"); var b = mori.seq("123"); var f = function(x, y) { return mori.list(x, x + y); }; mori.mapcat(f, a, b); // => ("a", "a1", "b", "b2", "c", "c3") mori.reduce(mori.concat, mori.map(f, a, b)); // => ("a", "a1", "b", "b2", "c", "c3")
filter mori.filter(pred, coll)
Return a lazy sequence representing the original
collection filtered of elements which did not return a truthy
value for pred
. Note that Mori has a stricter
notion of truth than JavaScript. Only false, undefined, and null
are considered false values.
var a = [0,1,2,3,4,5,7,7,9]; mori.filter(mori.isEven, a0); // => (0 2 4 6 8)
remove mori.remove(pred, coll)
The inverse of filter. Return a lazy sequence representing the original
collction filtered of elements which returned a truthy
value for pred
. Note that Mori has a stricter
notion of truth than JavaScript. Only false, undefined, and null
are considered false values.
var a = [0,1,2,3,4,5,6,7,9]; mori.remove(mori.isEven, a0); // => (1 3 5 7 9)
reduce mori.reduce(f, [intial], coll)
Accumulate a collection into a single value. f
should be a function of two arguments, the first will be the
accumulator, the second will be next value in the sequence.
var a = mori.range(10); mori.reduce(mori.sum, 0, r); // => 45
reduceKV mori.reduceKV(f, [initial], map)
A variant of reduce
for map-like collections,
specifically hash maps and vectors.
var f = function(acc, key, val) { return acc + "(" + key + ":" + val + ")"; }; var m = mori.hashMap("foo", 1, "bar", 2); mori.reduceKV(f, "", m); // => "(foo:1)(bar:2)" var v = mori.vector(5, 7); mori.reduceKV(f, "", v); // => "(0:5)(1:7)"
take mori.take(n, coll)
Takes n elements from a colletion. Note that coll
could be an infinite sequence. This function returns
a lazy sequence.
var a = mori.range(); // infinite sequence mori.take(10, r); // => (0 1 2 3 4 5 6 7 8 9)
takeWhile mori.takeWhile(pred, coll)
Takes elements from a collection as long as the
function pred
returns a value other
than false
, null
or undefined
. Returns a lazy sequence.
The following example is in CoffeeScript:
a = [0,1,2,3,4,5,6,7,8,9] mori.takeWhile ((n) -> n < 5), r // => (0 1 2 3 4)
drop mori.drop(n, coll)
Drop n elements from a collection. Returns a lazy sequence.
var a = [0,1,2,3,4,5,6,7,8,9] mori.drop(5, a); // => (5 6 7 8 9)
dropWhile mori.interleave(pred, coll)
Drops elements from a collection as long as the
function pred
returns a value other
than false
, null
or undefined
. Returns a lazy sequence.
The following example is in CoffeeScript:
a = [0,1,2,3,4,5,6,7,8,9] mori.dropWhile ((n) -> n < 5), a // => (5 6 7 8 9)
some mori.some(pred, coll)
Applies the function pred
to the elements of the
collection in order and returns the first result which is
not false
, null
or undefined
.
var a = [1,2,3,4,5,6,7,8,9]; var f = function(x) { return x % 5 == 0 && x * x; }; mori.some(f, a); // => 25
every mori.every(pred, coll)
Returns true if the result of applying the
function pred
to an element of the collection is
never false
, null
or undefined
.
var a = [1,2,3,4,5,6,7,8,9]; var f = function(x) { return mori.isEven(x); }; var g = function(x) { return mori.isEven(x) || mori.isOdd(x); }; mori.every(f, a); // => false mori.every(g, a); // => true
sort mori.sort([cmp], coll)
Sorts the collection and returns a sequence. The comparison function to be used can be given as the first argument.
var a = [4,6,2,7,1,0,9,5,8,3] var f = function(a, b) { return b - a; }; mori.sort(a); // => (0 1 2 3 4 5 6 7 8 9) mori.sort(f, a); // => (9 8 7 6 5 4 3 2 1 0)
sortBy mori.sortBy(keyfn, [cmp], coll)
Sorts the collection by the values of keyfn
on the
elements and returns a sequence. The comparison function to be
used can be given as the first argument.
var a = [0,1,2,3,4,5,6] var kf = function(x) { return x * 5 % 7; }; var f = function(a, b) { return b - a; }; mori.map(kf, a); // => (0 5 3 1 6 4 2) mori.sortBy(kf, a); // => (0 3 6 2 5 1 4) mori.sortBy(kf, f, a); // => (4 1 5 2 3 6 0)
interpose mori.interpose(x, coll)
Interpose a value between all elements of a collection.
var a = [1,2,3] mori.interpose("foo", a) // => (1 "foo" 2 "foo" 3)
interleave mori.interleave(coll0, coll1, ...)
Interleave two or more collections. The size of the resulting lazy sequence is determined by the smallest collection.
var ns = [1,2,3]; var as = ["a", "b", "c"]; mori.interleave(ns, as); // => (1 "a" 2 "b" 3 "c")
iterate mori.iterate(f, x)
Creates a lazy sequences of x, f(x), f(f(x)), ...
mori.iterate(mor.inc, 0); // => (0 1 2 3 4 5 ...)
repeat mori.repeat([n], x)
Return a lazy of sequence of the value repeated.
If given n
, the value will only be
repeated n times.
The following example is in CoffeeScript:
m = mori foos = m.repeat("foo") m.zipmap [1,2,3], foos // => {1 "foo", 2 "foo", 3 "foo"}
repeatedly mori.repeatedly([n], f)
Return a lazy of sequence of calling f
, a
function which takes no arguments (presumably for side effects).
If given n
, the function will only be
repeated n times.
mori.repeatedly(5, Math.random); // => (... 5 random floating point numbers ...)
partition mori.partition(n, [step], [pad], coll)
Partition a seqable collection into groups of n
items. An optional step
parameter may be provided to specify the amount of overlap. An additional pad
element can be provided when the final group of items is too small.
var m = mori, arr = [0,1,2,3,4,5,6,7,8,9], ps = m.partition(2, arr); m.intoArray(m.map(m.intoArray, ps)); // => [[0,1],[2,3],[4,5],[6,7],[8,9]] ps = m.partition(2, 1, arr); m.intoArray(m.map(m.intoArray, ps)); // => [[0,1],[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,8],[8,9]]
partitionBy mori.partitionBy(f, coll)
Partition a seqable collection with a new group being started
whenever the value of the function f
changes.
var v = mori.vector("foo", "bar", "baz", "grapefruit"); var f = function(s) { return s[0]; } mori.partitionBy(f, v); // => (("foo") ("bar" "baz") ("grapefruit"))
groupBy mori.groupBy(f, coll)
Returns a map of the items grouped by the result of applying
f
to the element.
function evenOdd(n) { return mori.isEven(n) ? "even" : "odd"; } var r = mori.range(10); mori.groupBy(evenOdd, r); // => {"even" (0 2 4 6 8) "odd" (1 3 5 7 9)}
Helpers
primSeq mori.primSeq(seqable, [index])
There are many array-like JavaScript objects which
are not actually arrays. To give these objects a uniform
interface you can wrap them with mori.primSeq
.
The optional argument index
may be used to
specify an offset. Note this is not necesary for arrays or
strings.
function foo() { var args = mori.primSeq(arguments); var f = mori.first(args); }
identity mori.identity(x)
A function which simply returns its argument
mori.identity(5); // => 5
constantly mori.constantly(x)
Makes a function that takes any number of arguments and
simply returns x
.
mori.map(mori.constantly("foo"), mori.range(4)); // => ("foo" "foo" "foo" "foo")
inc mori.inc(n)
Adds one to its argument.
mori.inc(1); // => 2
dec mori.dec(n)
Subtracts one from its argument.
mori.dec(1); // => 0
sum mori.sum(a, b)
Add its two arguments together. Useful with mori.reduce
.
mori.sum(1,2); // => 3 mori.reduce(mori.sum, 0, mori.range(10)); // 45
isEven mori.isEven(n)
Returns true if the argument is divisible by 2.
isOdd mori.isOdd(n)
Returns true if the argument is not divisible by 2.
comp mori.comp(f, g)
Function composition. The result of mori.comp(f,
g)(x)
is the same as f(g(x))
.
mori.map(mori.comp(mori.isOdd, mori.inc), [1, 2, 3]); // => (false true false)
juxt mori.juxt(f0, f1, ...)
Takes a series of functions and creates a single function which represents their "juxtaposition". When this function is called, will return the result of each function applied to the arguments in a JavaScript array.
f = mori.juxt(mori.first, mori.last); f([1, 2, 3, 4, 5, 6]); // => [ 1, 6 ] (a javascript array)
knit mori.knit(f0, f1, ...)
This allows you to create functions that work on a heterogenous
collection and return a new collection of the same
arity. It is an relative
of juxt
. Like juxt
it takes a series of functions and returns a new
function. Unlike juxt
the resulting
function takes a sequence. The functions and sequence
are zipped together and invoked.
var f = mori.knit(function (s) { return s.toLowerCase(); }, function (s) { return s.toUpperCase(); }); f(["FoO", "bAr"]); // => ["foo", "BAR"]
pipeline mori.pipeline(x, f0, f1, ...)
Allows threading a value through a series of functions.
var _ = mori; _.pipeline( _.vector(1,2,3), _.curry(_.conj, 4), _.curry(_.conj, 5) ); // => [1,2,3,4,5]
partial mori.partial(f, x, y, ...)
Partial application. Will return a new function in which the provided arguments have been partially applied.
var _ = mori; var f = _.partial(_.conj, _.vector(1,2,3)); f(4); // => [1,2,3,4] f(5); // => [1,2,3,5]
curry mori.curry(f, x, y, ...)
Curry arguments to a function.
var _ = mori; var f = _.curry(_.conj, 4); f(_.vector(1,2,3)); // => [1,2,3,4]
fnil mori.fnil(f, x, y, ...)
Takes a function f and returns a new function that upon receiving an argument, if null, will be replaced with x. fnil may take up to three arguments.
var _ = mori; var f = function(x) { return _.updateIn(x, ["count"], _.fnil(_.inc, 0)); }; f(_.hashMap()); // => {"count", 1}
toClj mori.toClj(x)
Recursively transforms JavaScript arrays into Mori vectors, and JavaScript objects into Mori maps.
var data = {foo: "bar"}; mori.toClj(data); // => {"foo", "bar"}
toJs mori.toJs(x)
Recursively transforms Mori values to JavaScript. sets/vectors/lists/queues become Arrays, Keywords and Symbol become Strings, Maps become Objects. Arbitrary keys are encoded to by key->js.
var data = mori.hashMap("foo", "bar"); mori.toJs(data); // => {"foo", "bar"} of type js/Object